home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / GNU / GNUPLOTsrc.lha / scanner.c < prev    next >
C/C++ Source or Header  |  1996-01-22  |  11KB  |  396 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: scanner.c,v 1.49 1995/12/02 22:04:37 drd Exp $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - scanner.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *  
  20.  * This software is provided "as is" without express or implied warranty.
  21.  * 
  22.  *
  23.  * AUTHORS
  24.  * 
  25.  *   Original Software:
  26.  *     Thomas Williams,  Colin Kelley.
  27.  * 
  28.  *   Gnuplot 2.0 additions:
  29.  *       Russell Lang, Dave Kotz, John Campbell.
  30.  *
  31.  *   Gnuplot 3.0 additions:
  32.  *       Gershon Elber and many others.
  33.  * 
  34.  * There is a mailing list for gnuplot users. Note, however, that the
  35.  * newsgroup 
  36.  *    comp.graphics.gnuplot 
  37.  * is identical to the mailing list (they
  38.  * both carry the same set of messages). We prefer that you read the
  39.  * messages through that newsgroup, to subscribing to the mailing list.
  40.  * (If you can read that newsgroup, and are already on the mailing list,
  41.  * please send a message info-gnuplot-request@dartmouth.edu, asking to be
  42.  * removed from the mailing list.)
  43.  *
  44.  * The address for mailing to list members is
  45.  *       info-gnuplot@dartmouth.edu
  46.  * and for mailing administrative requests is 
  47.  *       info-gnuplot-request@dartmouth.edu
  48.  * The mailing list for bug reports is 
  49.  *       bug-gnuplot@dartmouth.edu
  50.  * The list of those interested in beta-test versions is
  51.  *       info-gnuplot-beta@dartmouth.edu
  52.  */
  53.  
  54. #include <ctype.h>
  55. #include <math.h>
  56. #include "plot.h"
  57.  
  58. static int get_num __P((char str[]));
  59. static void substitute __P((char *str, int max));
  60.  
  61. #ifdef AMIGA_AC_5
  62. #define O_RDONLY    0
  63. int open(const char * _name, int _mode, ...);
  64. int close(int);
  65. #endif
  66.  
  67. #ifdef vms
  68.  
  69. #include <descrip.h>
  70.  
  71. #define MAILBOX "PLOT$MAILBOX"
  72. #define pclose(f) fclose(f)
  73.  
  74. #endif /* vms */
  75.  
  76.  
  77. #define isident(c) (isalnum(c) || (c) == '_')
  78.  
  79. #ifndef STDOUT
  80. #define STDOUT 1
  81. #endif
  82.  
  83. #define LBRACE '{'
  84. #define RBRACE '}'
  85.  
  86. #define APPEND_TOKEN {token[t_num].length++; current++;}
  87.  
  88. #define SCAN_IDENTIFIER while (isident(expression[current + 1]))\
  89.                 APPEND_TOKEN
  90.  
  91. static int t_num;    /* number of token I'm working on */
  92.  
  93. /*
  94.  * scanner() breaks expression[] into lexical units, storing them in token[].
  95.  *   The total number of tokens found is returned as the function value.
  96.  *   Scanning will stop when '\0' is found in expression[], or when token[]
  97.  *     is full.
  98.  *
  99.  *     Scanning is performed by following rules:
  100.  *
  101.  *    Current char    token should contain
  102.  *     -------------    -----------------------
  103.  *    1.  alpha,_    all following alpha-numerics
  104.  *    2.  digit    0 or more following digits, 0 or 1 decimal point,
  105.  *                0 or more digits, 0 or 1 'e' or 'E',
  106.  *                0 or more digits.
  107.  *    3.  ^,+,-,/    only current char
  108.  *        %,~,(,)
  109.  *        [,],;,:,
  110.  *        ?,comma
  111.  *          $           for using patch (div)
  112.  *    4.  &,|,=,*    current char; also next if next is same
  113.  *    5.  !,<,>    current char; also next if next is =
  114.  *    6.  ", '    all chars up until matching quote
  115.  *    7.  #        this token cuts off scanning of the line (DFK).
  116.  *
  117.  *            white space between tokens is ignored
  118.  */
  119. int scanner(expression)
  120. char expression[];
  121. {
  122. register int current;    /* index of current char in expression[] */
  123. register int quote;
  124. char brace;
  125.  
  126.     for (current = t_num = 0; expression[current] != '\0'; current++) {
  127. again:
  128.         if(t_num+1>=token_table_size) {
  129.         /* leave space for dummy end token */
  130.             extend_token_table();
  131.         }
  132.         if (isspace(expression[current]))
  133.             continue;                        /* skip the whitespace */
  134.         token[t_num].start_index = current;
  135.         token[t_num].length = 1;
  136.         token[t_num].is_token = TRUE;    /* to start with...*/
  137.  
  138.         if (expression[current] == '`') {
  139.             substitute(&expression[current],MAX_LINE_LEN - current);
  140.             goto again;
  141.         }
  142.         /* allow _ to be the first character of an identifier */
  143.         if (isalpha(expression[current]) || expression[current] == '_') {
  144.             SCAN_IDENTIFIER;
  145.         } else if (isdigit(expression[current]) || expression[current] == '.'){
  146.             token[t_num].is_token = FALSE;
  147.             token[t_num].length = get_num(&expression[current]);
  148.             current += (token[t_num].length - 1);
  149.         } else if (expression[current] == LBRACE) {
  150.             token[t_num].is_token = FALSE;
  151.             token[t_num].l_val.type = CMPLX;
  152. #ifdef __PUREC__
  153.             { char    l[80];
  154.             if ((sscanf(&expression[++current],"%lf,%lf%[ }]s",
  155.                 &token[t_num].l_val.v.cmplx_val.real,
  156.                 &token[t_num].l_val.v.cmplx_val.imag,
  157.                 &l)    != 3) || (!strchr(l, RBRACE))  )
  158.                     int_error("invalid complex constant",t_num);
  159.             }
  160. #else
  161.             if ((sscanf(&expression[++current],"%lf , %lf %c",
  162.                 &token[t_num].l_val.v.cmplx_val.real,
  163.                 &token[t_num].l_val.v.cmplx_val.imag,
  164.                 &brace) != 3) || (brace != RBRACE))
  165.                     int_error("invalid complex constant",t_num);
  166. #endif
  167.             token[t_num].length += 2;
  168.             while (expression[++current] != RBRACE) {
  169.                 token[t_num].length++;
  170.                 if (expression[current] == '\0')            /* { for vi % */
  171.                     int_error("no matching '}'", t_num);
  172.             }
  173.         } else if (expression[current] == '\'' || expression[current] == '\"'){
  174.             token[t_num].length++;
  175.             quote = expression[current];
  176.             while (expression[++current] != quote) {
  177.                 if (!expression[current]) {
  178.                     expression[current] = quote;
  179.                     expression[current+1] = '\0';
  180.                     break;
  181.                 } else
  182.                     token[t_num].length++;
  183.             }
  184.         } else switch (expression[current]) {
  185.              case '#':        /* DFK: add comments to gnuplot */
  186.                   goto endline; /* ignore the rest of the line */
  187.             case '^':
  188.             case '+':
  189.             case '-':
  190.             case '/':
  191.             case '%':
  192.             case '~':
  193.             case '(':
  194.             case ')':
  195.             case '[':
  196.             case ']':
  197.             case ';':
  198.             case ':':
  199.             case '?':
  200.             case ',':
  201.             case '$': /* div */
  202.                 break;
  203.             case '&':
  204.             case '|':
  205.             case '=':
  206.             case '*':
  207.                 if (expression[current] == expression[current + 1])
  208.                     APPEND_TOKEN;
  209.                 break;
  210.             case '!':
  211.             case '<':
  212.             case '>':
  213.                 if (expression[current + 1] == '=')
  214.                     APPEND_TOKEN;
  215.                 break;
  216.             default:
  217.                 int_error("invalid character",t_num);
  218.             }
  219.         ++t_num;    /* next token if not white space */
  220.     }
  221.  
  222. endline:                    /* comments jump here to ignore line */
  223.  
  224. /* Now kludge an extra token which points to '\0' at end of expression[].
  225.    This is useful so printerror() looks nice even if we've fallen off the
  226.    line. */
  227.  
  228.         token[t_num].start_index = current;
  229.         token[t_num].length = 0;
  230.         /* print 3+4  then print 3+  is accepted without
  231.          * this, since string is ignored if it is not
  232.          * a token
  233.          */
  234.         token[t_num].is_token=TRUE;
  235.     return(t_num);
  236. }
  237.  
  238.  
  239. static int get_num(str)
  240. char str[];
  241. {
  242. register int count = 0;
  243. register long lval;
  244.  
  245.     token[t_num].is_token = FALSE;
  246.     token[t_num].l_val.type = INTGR;        /* assume unless . or E found */
  247.     while (isdigit(str[count]))
  248.         count++;
  249.     if (str[count] == '.') {
  250.         token[t_num].l_val.type = CMPLX;
  251.         while (isdigit(str[++count]))    /* swallow up digits until non-digit */
  252.             ;
  253.         /* now str[count] is other than a digit */
  254.     }
  255.     if (str[count] == 'e' || str[count] == 'E') {
  256.         token[t_num].l_val.type = CMPLX;
  257. /* modified if statement to allow + sign in exponent
  258.    rjl 26 July 1988 */
  259.         count++;
  260.         if (str[count] == '-' || str[count] == '+')
  261.             count++;
  262.         if (!isdigit(str[count])) {
  263.             token[t_num].start_index += count;
  264.             int_error("expecting exponent",t_num);
  265.         }
  266.         while (isdigit(str[++count]))
  267.             ;
  268.     }
  269.     if (token[t_num].l_val.type == INTGR) {
  270.          lval = atol(str);
  271.         if ((token[t_num].l_val.v.int_val = lval) != lval)
  272.             int_error("integer overflow; change to floating point",t_num);
  273.     } else {
  274.         token[t_num].l_val.v.cmplx_val.imag = 0.0;
  275.         token[t_num].l_val.v.cmplx_val.real = atof(str);
  276.     }
  277.     return(count);
  278. }
  279.  
  280. #if defined(unix) || defined(vms) || defined(PIPES) || (defined(ATARI) && defined(__PUREC__)) || (defined(MTOS) && defined(__PUREC__))
  281.  
  282. static void substitute(str,max)        /* substitute output from ` ` */
  283. char *str;
  284. int max;
  285. {
  286. register char *last;
  287. register int i,c;
  288. register FILE *f;
  289. #ifdef AMIGA_AC_5
  290. int fd;
  291. #else
  292. #if (defined(ATARI) && defined(__PUREC__)) || (defined(MTOS) && defined(__PUREC__))
  293. char    *atari_tmpfile;
  294. char    *atari_pgm[MAX_LINE_LEN+100];
  295. #else
  296. extern FILE *popen();
  297. #endif /* ATARI && PUREC || MTOS && PUREC */
  298. #endif /* AMIGA_AC_5 */
  299. static char pgm[MAX_LINE_LEN+1],output[MAX_LINE_LEN+1];
  300.  
  301. #ifdef vms
  302. int chan, one = 1;
  303. static $DESCRIPTOR(pgmdsc,pgm);
  304. static $DESCRIPTOR(lognamedsc,MAILBOX);
  305. #endif /* vms */
  306.  
  307.     i = 0;
  308.     last = str;
  309.     while (*(++last) != '`') {
  310.         if (*last == '\0')
  311.             int_error("unmatched `",t_num);
  312.         pgm[i++] = *last;
  313.     }
  314.     pgm[i] = '\0';        /* end with null */
  315.     max -= strlen(last);    /* max is now the max length of output sub. */
  316.   
  317. #ifdef vms
  318.       pgmdsc.dsc$w_length = i;
  319.        if (!((vaxc$errno = sys$crembx(0,&chan,0,0,0,0,&lognamedsc)) & 1))
  320.            os_error("sys$crembx failed",NO_CARET);
  321.    
  322.        if (!((vaxc$errno = lib$spawn(&pgmdsc,0,&lognamedsc,&one)) & 1))
  323.            os_error("lib$spawn failed",NO_CARET);
  324.    
  325.        if ((f = fopen(MAILBOX,"r")) == NULL)
  326.            os_error("mailbox open failed",NO_CARET);
  327. #else /* vms */
  328. #if (defined(ATARI) && defined(__PUREC__)) || (defined(MTOS) && defined(__PUREC__))
  329.         if (system(NULL) == 0)
  330.             os_error("no command shell", NO_CARET);
  331.         if ((strlen(atari_tmpfile) + strlen(pgm) + 5) > MAX_LINE_LEN+100)
  332.             os_error("sorry, command to long", NO_CARET);
  333.         atari_tmpfile = tmpnam(NULL);
  334.         strcpy(atari_pgm, pgm);
  335.         strcat(atari_pgm, " >> ");
  336.         strcat(atari_pgm, atari_tmpfile);
  337.         system(atari_pgm);
  338.         if ((f = fopen(atari_tmpfile, "r")) == NULL)
  339. #else
  340. #ifdef AMIGA_AC_5
  341.       if ((fd = open(pgm,"O_RDONLY")) == -1)
  342. #else
  343.       if ((f = popen(pgm,"r")) == NULL)
  344. #endif
  345. #endif    /* ATARI && PUREC || MTOS && PUREC */
  346.           os_error("popen failed",NO_CARET);
  347. #endif /* vms */
  348.  
  349.     i = 0;
  350.     while ((c = getc(f)) != EOF) {
  351.         output[i++] = ((c == '\n') ? ' ' : c);    /* newlines become blanks*/
  352.         if (i == max) {
  353. #ifdef AMIGA_AC_5
  354.             (void) close(fd);
  355. #else
  356. #if (defined(ATARI) && defined(__PUREC__)) || (defined(MTOS) && defined(__PUREC__))
  357.             (void) fclose(f);
  358.             (void) unlink(atari_tmpfile);
  359. #else
  360.             (void) pclose(f);
  361. #endif /* ATARI && PUREC || MTOS && PUREC */
  362. #endif
  363.             int_error("substitution overflow", t_num);
  364.         }
  365.     }
  366. #ifdef AMIGA_AC_5
  367.     (void) close(fd);
  368. #else
  369. #if (defined(ATARI) && defined(__PUREC__)) || (defined(MTOS) && defined(__PUREC__))
  370.     (void) fclose(f);
  371.     (void) unlink(atari_tmpfile);
  372. #else
  373.     (void) pclose(f);
  374. #endif /* ATARI && PUREC || MTOS && PUREC */
  375. #endif
  376.  
  377.     if (i + strlen(last) > max)
  378.         int_error("substitution overflowed rest of line", t_num);
  379.     (void) strncpy(output+i,last+1,MAX_LINE_LEN-i);
  380.                                     /* tack on rest of line to output */
  381.     (void) strcpy(str,output);                /* now replace ` ` with output */
  382.     screen_ok = FALSE;
  383. }
  384.  
  385. #else /* unix || vms || PIPES || ATARI && PUREC */
  386.  
  387. static void substitute(str,max)
  388. char *str;
  389. int max;
  390. {
  391.     char line[100];
  392.  
  393.     int_error( strcat(strcpy(line,"substitution not supported by "),OS),t_num);
  394. }
  395. #endif /* unix || vms || PIPES || ATARI && PUREC */
  396.